﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using Schuifpuzzel.MyKinect;
using Schuifpuzzel.Model;
using System.Windows.Media.Animation;
using System.Timers;
using System.Windows.Threading;
using System.Windows.Media.Imaging;

namespace Schuifpuzzel
{
    /// <summary>
    /// Interaction logic for SlidePuzzle.xaml
    /// </summary>
    /// 
    public delegate void PuzzleEndHandler();

    public partial class SlidePuzzleScreen
    {
        private enum FontAnimationStatus
        {
            NotPlaying,
            Growing,
            Shrinking
        }

        private SlidePuzzle _puzzle;
        private HighScoreList _highscores;
        private const int EmptyRectangleNumber = 8;

        private Rectangle[] _rectangles = new Rectangle[9];
        private Rectangle[] _backgroundRectangles = new Rectangle[9];
        private double[] _leftValues = new double[9];
        private double[] _topValues = new double[9];

        private Storyboard _myStoryboard;
        private Boolean _playingAnimation;
        private const double AnimationDuration = 0.25;
        private Timer _timer = new Timer(1000);
        private Timer _timer2 = new Timer(15);
        private int _elapsedTime;
        private double _handPositionStart;
        private double scale;
        public event PuzzleEndHandler PuzzleEnd;
        private FontAnimationStatus fontAnimationStatus = FontAnimationStatus.NotPlaying;
        private Boolean firstGrowAnimation ;
        public int PicHeight { get; set; }
        public int PicWidth { get; set; }
        public static BitmapSource _playerImage;
        
        public HighScoreList HighScoreList
        {
            set
            {
                _highscores = value;
            }

            get
            {
                return _highscores;
            }

        }

        /// <summary>
        /// Initialize slidepuzzle and highscore list and create the storyboard for the puzzle animations.
        /// </summary>
        public SlidePuzzleScreen()
        {
            InitializeComponent();
            _puzzle = new SlidePuzzle(EmptyRectangleNumber);
            _highscores = new HighScoreList();

            _myStoryboard = (Storyboard)this.Resources["myStoryboard"];
            _myStoryboard.Completed += MyStoryboardCompleted;
            
            FillRectangles();
            RefreshRectangles(false);
        }

        /// <summary>
        /// Updates the score text.
        /// </summary>
        void _puzzle_PropertyChanged()
        {
            Dispatcher.BeginInvoke(new Action(() => txtScore.Text = "Time: " + _puzzle.TimeElapsed + "\nMoves: " + _puzzle.Moves), DispatcherPriority.Normal);

        }

        /// <summary>
        /// Whenever an arm movement gets detected we check what kind of movement happened.
        /// </summary>
        /// <param name="handIsInFrontOfBody">Hand isn't visible</param>
        /// <param name="xCoordinates">The new x coordinate of the hand</param>
        /// <param name="yCoordinates">The new y coordinate of the hand</param>
        /// <param name="detectingSwipe">Indicates whether a swipe is being detected</param>

        void SlidePuzzleScreen_ArmPositionDetected(bool handDetected, double xCoordinates, double yCoordinates, bool detectingSwipe)
        {
            if (!handDetected)
            {
                //Display message to stretch arm
                //txtInstruction.Text = "Reach out your hand to control the puzzle";

                trackingGlobe.startPosition.Fill = Brushes.Black;
                trackingGlobe.handPosition.Visibility = Visibility.Hidden;
                trackingGlobe.MakeArrowsWhite();
            }
            else
            {
                trackingGlobe.handPosition.Visibility = Visibility.Visible;
               trackingGlobe.startPosition.Fill = detectingSwipe ? Brushes.Black : Brushes.Red;

                MoveHand(xCoordinates, yCoordinates);
                if (detectingSwipe) trackingGlobe.MakeArrowsWhite();
            }

        }

        /// <summary>
        /// Moves the dot to imitate the detected hand movement.
        /// </summary>
        /// <param name="x">X coordinate to move the handtracker to, after we scale it to the windowsize.</param>
        /// <param name="y">Y coordinate to move the handtracker to, after we scale it to the windowsize.</param>
        public void MoveHand(double x, double y)
        {
            double scaledY = _handPositionStart - scale * y  ;
            double scaledX = _handPositionStart + scale * x  ;
            if (scaledY > trackingGlobe.mainCanvas.Width)
            {
                scaledY = trackingGlobe.mainCanvas.Width - trackingGlobe.handPosition.Width;
            }
            if (scaledY < 0)
            {
                scaledY = 0;
            }
            if (scaledX > trackingGlobe.mainCanvas.Width)
            {
                scaledX = trackingGlobe.mainCanvas.Width - trackingGlobe.handPosition.Width;
            }
            if (scaledX < 0)
            {
                scaledX = 0;
            }
            Canvas.SetTop(trackingGlobe.handPosition, scaledY);
            Canvas.SetLeft(trackingGlobe.handPosition, scaledX);
        }

        /// <summary>
        /// Starts the puzzle, binds the timers and takes a picture
        /// </summary>
        public void Start()
        {
            
            _timer.Elapsed += TimerElapsedToStartPuzzle;
            _timer2.Elapsed += TimerElapsedToChangeFont;
            _elapsedTime = 0;
            _timer.Enabled = true;          
            _playerImage = KinectColorViewer.LastDetectedImage.Clone();
            txtCongratulations.Visibility = Visibility.Hidden;
            txtScore.Text = "Time: " + 0 + "\nMoves: " + 0;
            scoreScreen.Visibility = Visibility.Hidden;
            _playingAnimation = false;            
            _myStoryboard.Stop();
            _myStoryboard.Children.Clear();
            trackingGlobe.MakeArrowsWhite();
            trackingGlobe.startPosition.Fill = Brushes.Black;
            MoveHand(0,0);
            ResetPuzzle();
            //scoreScreen.Visibility = Visibility.Visible;
           
        }

        /// <summary>
        /// Stops the puzzle and unbinds the events
        /// </summary>
        public void Stop()
        {
            if (PuzzleEnd != null)
            {
                KinectForPuzzle.GetInstance().GestureDetected -= GestureDetected;
                _puzzle.PropertyChanged -= _puzzle_PropertyChanged;
                _timer2.Elapsed -= TimerElapsedToChangeFont;
                KinectForPuzzle.GetInstance().ArmPositionDetected -= SlidePuzzleScreen_ArmPositionDetected;
                _timer.Enabled = false;
                _playingAnimation = false;
                _myStoryboard.Stop();
                _myStoryboard.Children.Clear();              
                _timer.Elapsed -= TimerElapsedToStartPuzzle;
                PuzzleEnd();
            }
        }

        /// <summary>
        /// Start the animation to congratulate the user.
        /// </summary>
        private void StartCongratulationsAnimation()
        {
            fontAnimationStatus = FontAnimationStatus.Growing;
            txtCongratulations.FontSize = 1;
            firstGrowAnimation = true;
            txtCongratulations.Visibility = Visibility.Visible;
            _timer2.Enabled = true;
        }


       
        /// <summary>
        /// Resets the puzzles and refreshes the UI
        /// </summary>
        public void ResetPuzzle()
        {
            _puzzle.ResetPuzzle();
            // KinectForPuzzle.GetInstance().ArmStretchedDetected += new ArmStretchedDetectedHandler(kinect_ArmStretchedDetected);
            RefreshRectangles(false);
        }

        /// <summary>
        /// Method called when a gesture is detected.
        /// </summary>
        /// <param name="detectedMovement"></param>
        void GestureDetected(MoveDirection detectedMovement)
        {
            
            Boolean isSolved;

            //Don't do anything if animation is still playing animation
            if (!_playingAnimation)
            {
                //Make the move, checkf if puzzle is solved and refresh UI
                isSolved = _puzzle.MoveEmptyTile(detectedMovement);
                trackingGlobe.MakeArrowWhite(detectedMovement);
                RefreshRectangles(true);
                if (isSolved)
                {
                    //Puzzle is solved
                    //Unbind events
                    trackingGlobe.MakeArrowsWhite();
                    trackingGlobe.startPosition.Fill = Brushes.Black;
                    trackingGlobe.handPosition.Visibility = Visibility.Hidden;
                    KinectForPuzzle.GetInstance().ArmPositionDetected -= SlidePuzzleScreen_ArmPositionDetected;
                    KinectForPuzzle.GetInstance().GestureDetected -= GestureDetected;
                    
                    //Play applaus sound and start animation
                    applause.Play();
                    StartCongratulationsAnimation();
                }
            }
        }

        /// <summary>
        /// Displays the result to the user
        /// </summary>
        private void ShowResult()
        {
            //Check if user made it into the high scores
            int highScorePosition = _highscores.CheckIfIsHighScore(_puzzle.TimeElapsed, _puzzle.Moves);

            scoreScreen.Visibility = Visibility.Visible;

            //Display the user's score
            scoreScreen.txtScore.Text = "Time: " + _puzzle.TimeElapsed + "\nMoves: " + _puzzle.Moves;

            //If user made it into the high scores
            if (highScorePosition > 0)
            {
                scoreScreen.txtName.Visibility = Visibility.Visible;
                scoreScreen.txtName.UpdateLayout();
                scoreScreen.txtName.Focus();
                scoreScreen.txtMessage.Text =
                    "Congratulations!\nYou have made it into the high scores!\nPlease enter your name.";

                scoreScreen.txtName.KeyDown += txtName_KeyDown;
            }
            else
            {
                scoreScreen.txtMessage.Text =
                        "Nicely done!\nUnfortunately you did not make it into the high scores.";
                scoreScreen.txtName.Visibility = Visibility.Hidden;
                _elapsedTime = 0;
                _timer.Elapsed += TimerElapsedToShowScore;
                _timer.Enabled = true;
            }
        }

        /// <summary>
        /// Method called after user when user pressed a key during the entering of his name
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void txtName_KeyDown(object sender, KeyEventArgs e)
        {

            if (e.Key == Key.Enter)
                //Check that name isn't empty or too long
                if (scoreScreen.txtName.Text != String.Empty)
                {
                    //Unbind keydown event and add score to the high scores
                    scoreScreen.txtName.KeyDown -= txtName_KeyDown;
                    _highscores.AddScore(new Score(_puzzle.TimeElapsed, _puzzle.Moves, scoreScreen.txtName.Text.Trim(), _playerImage));
                    scoreScreen.Visibility = Visibility.Collapsed;
                    scoreScreen.txtName.Text = "";
                    Stop();
                }
        }

        /// <summary>
        /// Sets the width and height for this screen
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        public void SetSize(double width, double height)
        {
            double heightForOneRectangle = height / 3;
            double widthForOneRectangle = width / 4;

            for (int i = 0; i < _rectangles.Length; i++)
            {
                if (i >= 3 && i < 6)
                {
                    _topValues[i] = heightForOneRectangle;
                }
                if (i >= 6)
                {
                    _topValues[i] = heightForOneRectangle * 2;
                }
                if (i == 1 || i == 4 || i == 7)
                {
                    _leftValues[i] = widthForOneRectangle;
                }
                if (i == 2 || i == 5 || i == 8)
                {
                    _leftValues[i] = widthForOneRectangle * 2;
                }

                _rectangles[i].Height = heightForOneRectangle;
                _rectangles[i].Width = widthForOneRectangle;

                _backgroundRectangles[i].Height = heightForOneRectangle;
                _backgroundRectangles[i].Width = widthForOneRectangle;
                Canvas.SetLeft(_backgroundRectangles[i], _leftValues[i]);
                Canvas.SetTop(_backgroundRectangles[i], _topValues[i]);
            }

            scoreScreen.Width = width /3 * 1.25;
            scoreScreen.Height = height /3 * 1.25;

            Canvas.SetLeft(scoreScreen, (width - scoreScreen.Width) /2);
            Canvas.SetTop(scoreScreen, (height - scoreScreen.Height) / 2);
            

            //Canvas.SetLeft(scoreScreen, width / 2 - 300);
            //Canvas.SetTop(scoreScreen, height / 2 - 150);

            gridCongratulations.Width = width;
            gridCongratulations.Height = height;


            double trackinGlobeSize;
            if(heightForOneRectangle< widthForOneRectangle)
            {
                trackinGlobeSize = heightForOneRectangle * 0.9;
            }
            else
            {
                trackinGlobeSize = widthForOneRectangle * 0.9;
            }
            Double tmp1 = (widthForOneRectangle - (trackinGlobeSize)) / 2;
            Double tmp2 = (widthForOneRectangle - heightForOneRectangle) / 2;            
            trackingGlobe.Margin = new Thickness(tmp1,tmp2,tmp1,0);            
            trackingGlobe.SetSize(trackinGlobeSize);

            _handPositionStart = (trackingGlobe.mainCanvas.Width - trackingGlobe.handPosition.Width) /2;
            scale = trackingGlobe.safeZone.Height / (2 * GestureDetector.ScaledDistanceToSwipe);

            gridForInstructions.Width = widthForOneRectangle;
            gridForInstructions.Height = heightForOneRectangle;
            
            

            //Canvas.SetBottom(colorViewer, (heightForOneRectangle - colorViewer.Height)/2);
            //Canvas.SetRight(colorViewer, (widthForOneRectangle - colorViewer.Width) / 2);


            oplossing.Width = heightForOneRectangle * 0.9;
            oplossing.Height = oplossing.Width * 0.75;

            oplossing.Margin = new Thickness(0, tmp2, 0, 0);
            //Canvas.SetBottom(oplossing, (heightForOneRectangle - oplossing.Height) / 2);
            //Canvas.SetRight(oplossing, (widthForOneRectangle - oplossing.Width) / 2);

            l1.X1 = oplossing.Width / 3;
            l1.X2 = oplossing.Width / 3;
            l1.Y1 = 0;
            l1.Y2 = oplossing.Height;

            l2.X1 = oplossing.Width / 3 * 2;
            l2.X2 = oplossing.Width / 3 * 2;
            l2.Y1 = 0;
            l2.Y2 = oplossing.Height;

            l3.X1 = 0;
            l3.X2 = oplossing.Width;
            l3.Y1 = oplossing.Height / 3;
            l3.Y2 = oplossing.Height / 3;

            l4.X1 = 0;
            l4.X2 = oplossing.Width;
            l4.Y1 = oplossing.Height / 3 * 2;
            l4.Y2 = oplossing.Height / 3 * 2;
            //oplossing.Visibility = Visibility.Hidden;
        }

        /// <summary>
        /// Places the rectangles on the canvas and fills them with part of the image
        /// </summary>
        private void FillRectangles()
        {
            Image image = (Image)this.Resources["MasterImage"];
            Image image2 = (Image)this.Resources["CronosImage"];
            ImageBrush cronosImageBrush;

            //row1
            _rectangles[0] = rec1;
            _rectangles[1] = rec2;
            _rectangles[2] = rec3;
            _backgroundRectangles[0] = recBackground1;
            _backgroundRectangles[1] = recBackground2;
            _backgroundRectangles[2] = recBackground3;
            //row2
            _rectangles[3] = rec4;
            _rectangles[4] = rec5;
            _rectangles[5] = rec6;
            _backgroundRectangles[3] = recBackground4;
            _backgroundRectangles[4] = recBackground5;
            _backgroundRectangles[5] = recBackground6;
            //row3
            _rectangles[6] = rec7;
            _rectangles[7] = rec8;
            _rectangles[8] = rec9;
            _backgroundRectangles[6] = recBackground7;
            _backgroundRectangles[7] = recBackground8;
            _backgroundRectangles[8] = recBackground9;

            //row1
            _rectangles[0].Fill = CreateImageBrush(image, 0, 0, 0.33333, 0.33333);
            _rectangles[1].Fill = CreateImageBrush(image, 0.33333, 0, 0.33333, 0.33333);
            _rectangles[2].Fill = CreateImageBrush(image, 0.66666, 0, 0.33333, 0.33333);
            //row2
            _rectangles[3].Fill = CreateImageBrush(image, 0, 0.33333, 0.33333, 0.33333);
            _rectangles[4].Fill = CreateImageBrush(image, 0.33333, 0.33333, 0.33333, 0.33333);
            _rectangles[5].Fill = CreateImageBrush(image, 0.66666, 0.33333, 0.33333, 0.33333);
            //row3
            _rectangles[6].Fill = CreateImageBrush(image, 0, 0.66666, 0.33333, 0.33333);
            _rectangles[7].Fill = CreateImageBrush(image, 0.33333, 0.66666, 0.33333, 0.33333);
            _rectangles[8].Fill = CreateImageBrush(image, 0.66666, 0.66666, 0.33333, 0.33333);

            _rectangles[EmptyRectangleNumber].Fill = null;

            cronosImageBrush = new ImageBrush { ImageSource = image2.Source };

            foreach (Rectangle rec in _backgroundRectangles)
            {
                rec.Fill = cronosImageBrush;
            }

        }

        /// <summary>
        /// Creates imagebrush of a part of the image
        /// </summary>
        /// <param name="image">The complete image</param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        private ImageBrush CreateImageBrush(Image image, double x, double y, double width, double height)
        {
            ImageBrush ib = new ImageBrush
                                {
                                    ImageSource = image.Source,
                                    Viewbox = new Rect(x, y, width, height),
                                    ViewboxUnits = BrushMappingMode.RelativeToBoundingBox,
                                    TileMode = TileMode.None
                                };

            return ib;
        }


        /// <summary>
        /// Refreshes the UI
        /// </summary>
        /// <param name="useAnimation">Indicates whether the movement should be instant or with animation</param>
        private void RefreshRectangles(Boolean useAnimation)
        {
            int tileNumber;
            _myStoryboard.Children.Clear();

            for (int i = 0; i < 9; i++)
            {
                tileNumber = _puzzle.GetTileNumber(i);

                if (useAnimation)
                {
                    DoubleAnimation animation1;
                    DoubleAnimation animation2;



                    animation1 = new DoubleAnimation
                                     {
                                         From = Canvas.GetLeft(_rectangles[tileNumber]),
                                         To = _leftValues[i],
                                         Duration = new Duration(TimeSpan.FromSeconds(AnimationDuration))
                                     };

                    Storyboard.SetTargetName(animation1, _rectangles[tileNumber].Name);
                    Storyboard.SetTargetProperty(animation1, new PropertyPath("(Canvas.Left)"));


                    animation2 = new DoubleAnimation
                                     {
                                         From = Canvas.GetTop(_rectangles[tileNumber]),
                                         To = _topValues[i],
                                         Duration = new Duration(TimeSpan.FromSeconds(AnimationDuration))
                                     };

                    Storyboard.SetTargetName(animation2, _rectangles[tileNumber].Name);
                    Storyboard.SetTargetProperty(animation2, new PropertyPath("(Canvas.Top)"));

                    _myStoryboard.Children.Add(animation1);
                    _myStoryboard.Children.Add(animation2);
                    _playingAnimation = true;
                    _myStoryboard.Begin();
                }
                else
                {
                    //int tempTileNumber = tileNumber;
                    //int tempi = i;
                    //Dispatcher.BeginInvoke(new Action(() => Canvas.SetLeft(_rectangles[tempTileNumber], _leftValues[tempi])), DispatcherPriority.Normal);
                    //Dispatcher.BeginInvoke(new Action(() => Canvas.SetTop(_rectangles[tempTileNumber], _topValues[tempi])), DispatcherPriority.Normal);
                    Canvas.SetLeft(_rectangles[tileNumber], _leftValues[i]);
                    Canvas.SetTop(_rectangles[tileNumber], _topValues[i]);
                }
            }


        }


        #region EventHandlers

        void TimerElapsedToStartPuzzle(object sender, ElapsedEventArgs e)
        {
            _elapsedTime++;
            if (_elapsedTime > 3)
            {
                _timer.Elapsed -= TimerElapsedToStartPuzzle;
                _timer.Enabled = false;
                _puzzle.StartPuzzle();
                _puzzle.PropertyChanged += _puzzle_PropertyChanged;
                Dispatcher.BeginInvoke(new Action(() => RefreshRectangles(false)), DispatcherPriority.Normal);
                KinectForPuzzle.GetInstance().GestureDetected += GestureDetected;
                KinectForPuzzle.GetInstance().ArmPositionDetected += SlidePuzzleScreen_ArmPositionDetected;
            }
        }

        void TimerElapsedToShowScore(object sender, ElapsedEventArgs e)
        {
            _elapsedTime++;
            if (_elapsedTime > 10)
            {
                _timer.Enabled = false;
                _timer.Elapsed -= TimerElapsedToShowScore;

                if (PuzzleEnd != null)
                {
                    Dispatcher.BeginInvoke(new Action(() => scoreScreen.Visibility = Visibility.Collapsed), DispatcherPriority.Normal);
                    Dispatcher.BeginInvoke(new Action(Stop), DispatcherPriority.Normal);
                }
            }



        }

        void TimerElapsedToChangeFont(object sender, ElapsedEventArgs e)
        {
            Dispatcher.BeginInvoke(new Action(ChangeFont), DispatcherPriority.Normal);
        }

        private void ChangeFont()
        {
            if (fontAnimationStatus == FontAnimationStatus.Growing)
            {
                if (txtCongratulations.FontSize < 120)
                {
                    txtCongratulations.FontSize++;

                }
                else
                {
                    fontAnimationStatus = FontAnimationStatus.Shrinking;
                }
            }
            else
            {
                if(firstGrowAnimation && txtCongratulations.FontSize < 60)
                {
                    firstGrowAnimation = false;
                    fontAnimationStatus = FontAnimationStatus.Growing;
                }
                else if (txtCongratulations.FontSize > 1)
                {
                    txtCongratulations.FontSize--;
                }
                else
                {
                    fontAnimationStatus = FontAnimationStatus.NotPlaying;
                    txtCongratulations.Visibility = Visibility.Hidden;
                    _timer2.Elapsed -= TimerElapsedToChangeFont;
                    _timer2.Enabled = false;
                    ShowResult();
                }
            }
        }

        void MyStoryboardCompleted(object sender, EventArgs e)
        {
            _playingAnimation = false;
        }

        private void Element_MediaEnded(object sender, RoutedEventArgs e)
        {
            applause.Stop();
        }

        #endregion


    }
}
